home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 5
/
Skunkware 5.iso
/
src
/
X11
/
xmbase-grok-1.2
/
query.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-06-25
|
7KB
|
256 lines
/*
* Do a query into the database, and store a query list into the card. A
* query list is an 1D array of row numbers in the database. Also set the
* number of cards from the query in nquery, and pick the card to be
* displayed first by setting qcurr.
*
* query_none(card)
* query_all(card)
* query_search(card, string)
* query_letter(card, letter)
* query_eval(card, expr)
*/
#include "config.h"
#include <X11/Xos.h>
#include <stdlib.h>
#include <errno.h>
#include <Xm/Xm.h>
#include "grok.h"
#include "form.h"
#include "proto.h"
#define SECT_OK(db,r) ((db)->currsect < 0 ||\
(db)->currsect == (db)->row[r]->section)
static BOOL alloc_query(CARD *, char **);
extern Widget toplevel; /* top-level shell for icon name */
extern int col_sorted_by; /* dbase is sorted by this column */
extern struct pref pref; /* global preferences */
/*
* delete query, summary becomes empty
*/
void query_none(
CARD *card) /* database and form */
{
if (card->query) /* clear old query */
free((void *)card->query);
card->query = 0;
card->nquery = 0;
card->qcurr = 0;
}
/*
* make sense out of the string, and do the appropriate search
*/
void query_any(
CARD *card, /* database and form */
char *string) /* query string */
{
if (!string || *string == '*' && !string[1])
query_all(card);
else if (*string == '(' || *string == '{')
query_eval(card, string);
else if (*string == '/')
query_search(card, string+1);
else
query_search(card, string);
}
/*
* put all cards into the summary
*/
void query_all(
CARD *card) /* database and form */
{
int r, n;
if (!alloc_query(card, 0))
return;
for (r=n=0; r < card->dbase->nrows; r++)
if (SECT_OK(card->dbase, r))
card->query[n++] = r;
card->nquery = n;
}
/*
* put all cards into the summary that contain a search string in any of
* the searchable items. The search is case-insensitive. This code will
* gleefully fail if run on a non-ascii system.
*/
void query_search(
CARD *card, /* database and form */
char *string) /* string to search for */
{
int r; /* row counter */
int i; /* item counter */
ITEM *item; /* item to check */
char *data; /* database string to test */
char *mask; /* for skipping unselected cards */
char search[1024]; /* lower-case search string */
register char *p, *q; /* copy and comparison pointers */
register char bit5 = 0x20; /* bit mask to smash case */
BOOL matched;
if (!alloc_query(card, &mask))
return;
for (p=string, q=search, i=0; *p && i < sizeof(search)-1; i++)
*q++ = *p++ | bit5;
*q = 0;
for (r=0; r < card->dbase->nrows; r++)
if (SECT_OK(card->dbase, r) && (!mask || mask[r])) {
matched = FALSE;
for (i=0; !matched && i < card->form->nitems; i++) {
item = card->form->items[i];
if (!IN_DBASE(item->type) || !item->search)
continue;
if (!(data = dbase_get(card->dbase, r,
item->column)))
continue;
for (p=data; *p; p++) {
if ((*p | bit5) != *search)
continue;
for (q=search; *q; q++, p++)
if ((*p | bit5) != *q)
break;
if (!*q) {
card->query[card->nquery++] =r;
matched = TRUE;
break;
}
p -= q - search;
}
}
}
if (mask)
free(mask);
}
/*
* put all cards into the summary whose last-sorted-by column begins with
* <letter>: 0..25=letter, 26=misc, 27=all. The search is case-insensitive.
*/
#define BLANK(c) ((c) && strchr("\t\n \"!#$%&'()*+,-./:;<=>?@[\\]^`{|}~", (c)))
void query_letter(
CARD *card, /* database and form */
int letter) /* 0=0..9, 1..26=a..z, 27=all */
{
int r; /* row counter */
char *data; /* database string to test */
char *mask; /* for skipping unselected cards */
if (letter == 27) {
query_all(card);
return;
}
if (!alloc_query(card, &mask))
return;
letter = letter == 26 ? 0 : letter+'A';
if (col_sorted_by >= card->dbase->maxcolumns-1)
col_sorted_by = 0;
for (r=0; r < card->dbase->nrows; r++)
if (SECT_OK(card->dbase, r) && (!mask || mask[r])) {
data = dbase_get(card->dbase, r, col_sorted_by);
while (data && BLANK(*data))
data++;
if ((!data || !*data || strchr("0123456789_", *data))
&& !letter)
card->query[card->nquery++] = r;
else if (data && letter)
while (*data) {
if ((*data & ~0x20) == letter) {
card->query[card->nquery++] = r;
break;
}
if (!pref.allwords)
break;
while (*data && !BLANK(*data))
data++;
while (BLANK(*data))
data++;
}
}
if (mask)
free(mask);
}
/*
* collect all cards that match the query expression. The expression is
* said to match if it does not return an empty string, or a string that,
* after stripping leading white space, begins with '0', 'f' or 'F'. The
* F's are supposed to mean False.
*/
void query_eval(
CARD *card, /* database and form */
char *expr) /* expression to apply to dbase */
{
char *val; /* return from expression eval */
char *mask; /* for skipping unselected cards */
if (!alloc_query(card, &mask))
return;
for (card->row=0; card->row < card->dbase->nrows; card->row++)
if (SECT_OK(card->dbase, card->row) &&
(!mask || mask[card->row])) {
if (!(val = evaluate(card, expr)))
break;
while (*val == ' ' || *val == '\t') val++;
if (*val && *val != '0' && *val != 'f' && *val != 'F')
card->query[card->nquery++] = card->row;
}
if (mask)
free(mask);
}
/*
* prepare for a query: destroy the previous query list, and allocate a new
* one with enough entries even if all cards match the query. The query list
* is a list of row numbers in the dbase. Return TRUE if everything went well.
* If <mask> is nonzero and incremental searches are enabled, create a string
* with one byte per card, which is 1 if that card was in the previous query.
* This makes it easy for the search routines to skip cards that have not been
* in the old query.
*/
static BOOL alloc_query(
CARD *card, /* database and form */
char **mask) /* where to store pointer to string */
{
if (mask) {
register int r;
*mask = 0;
if (pref.incremental && card->query
&& (*mask = malloc(card->dbase->nrows))) {
(void)memset(*mask, 0, card->dbase->nrows);
for (r=0; r < card->nquery; r++)
(*mask)[card->query[r]] = 1;
}
}
query_none(card);
if (!card->dbase->nrows)
return(FALSE);
if (!(card->query = (int*)malloc(card->dbase->nrows * sizeof(int*)))) {
create_error_popup(toplevel, errno,
"No memory for query result summary");
return(FALSE);
}
*card->query = 0;
return(TRUE);
}